/*
 * BaseDeliverer.java    0.0.1    10/dic/2009
 * 
 * Copyright (c) 2009 mentalsmash.org
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of the Apache License does not indicate that this project is
 * affiliated with the Apache Software Foundation.
 */
package org.mentalsmash.tazio.transparency.impl;

import java.io.IOException;
import java.util.List;

import org.mentalsmash.tazio.commons.identifiers.NID;
import org.mentalsmash.tazio.net.cs.client.Connection;
import org.mentalsmash.tazio.net.cs.client.ConnectionStateExeption;
import org.mentalsmash.tazio.transparency.commons.messages.TazioMessage;
import org.mentalsmash.tazio.transparency.interfaces.Deliverer;
import org.mentalsmash.tazio.transparency.interfaces.DeliveryFailureListener;
import org.mentalsmash.tazio.transparency.interfaces.Resolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class is the basic implementation of the {@link Deliverer} interface.
 * 
 * @version 0.0.1 10/dic/2009
 * @author Enrico Savioli <savioli.enrico@gmail.com>
 */
public class BaseDeliverer implements Deliverer {

    private final static Logger log = LoggerFactory
	    .getLogger(BaseDeliverer.class);

    private final int MAX_SENDING_REPETITIONS;

    private final DeliveryFailureListener deliveryFailureListener;
    private final Resolver resolver;
    private final TazioMessage toDeliver;
    private final List<NID> recipients;

    public BaseDeliverer(Resolver resolver, TazioMessage toDeliver,
	    List<NID> recipients, int MAX_SENDING_REPETITIONS) {

	this.MAX_SENDING_REPETITIONS = MAX_SENDING_REPETITIONS;
	this.deliveryFailureListener = (DeliveryFailureListener) resolver;
	this.resolver = resolver;
	this.toDeliver = toDeliver;
	this.recipients = recipients;

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.mentalsmash.tazio.transparency.Deliverer#doDeliver()
     */
    @Override
    public TazioMessage doDeliver() {

	Connection connection = null;
	TazioMessage answer = null;

	// System.out.println("i'm the deliverer..i've got to send the message to "+recipients.size()+" recipients");
	// int j = 0;
	String errorMsg = null;

	for (NID nid : recipients) {

	    // System.out.println("i'm the deliverer, sending the message to recipient number "+j);

	    log.debug("The sending of a message to " + nid + " is starting.");

	    for (int i = 0; i < MAX_SENDING_REPETITIONS; i++) {

		// System.out.println("repetition number "+i);

		try {
		    log.debug("Retrieving the connection for " + nid);
		    connection = resolver.resolve(nid);
		    
		} catch (TransparentDeliveryException e) {
		    // If the Replica Manager is unreachable contact a new one
		    // by breaking
		    // this cycle.
		    errorMsg = "The Replica Manager " + nid
			    + " was unreachable";
		    log.error(errorMsg, e);
		    break;
		}
		try {
		    if (connection == null) {
			errorMsg = "Unable to resolve " + nid + ";";
			log.error(errorMsg);
			break;
		    }
		    // System.out.println("sending the message");
		    answer = (TazioMessage) connection.sendRequest(toDeliver);
		    // System.out.println("message sent");
		} catch (ConnectionStateExeption e) {
		    errorMsg = "Error while sending the message: the connection is in "
			    + "an invalid state.";
		    log.error(errorMsg, e);
		    // if the connection is in an invalid state maybe it's
		    // better to try
		    // a new recipient
		    // continue;
		    break;

		} catch (IOException e) {
		    // If the timeout expires send the message again
		    errorMsg = "Input/Output error while sending the message.";
		    log.error(errorMsg, e);
		    continue;
		}

		// System.out.println("Answer received, returning it to the caller.");
		log.debug("Answer received, returning it to the caller.");
		return answer;
	    }

	    log.debug("Notifying the DeliveryFailureListener of the failure.");
	    deliveryFailureListener.failedDelivery(nid);

	}

	// If no server answered throw a RemoteException.
	log.warn("No server answered to the message: {}"
		+ this.toDeliver.getPayload());
	throw new TransparentDeliveryException(
		"No server answered to the message:"
			+ this.toDeliver.getPayload());

    }

}
